<?php

namespace App\Entities;

use App\Contracts\GeneratesUniqueHash;
use App\Contracts\HasIdColumn;
use App\Contracts\SystemComponent;
use App\Entities\Base\AbstractEntity;
use App\Entities\Base\User;
use Doctrine\ORM\Mapping as ORM;
use Illuminate\Support\Collection;

/**
 * App\Entities\VulnerabilityReferenceCode
 *
 * @ORM\Entity(repositoryClass="App\Repositories\VulnerabilityReferenceCodeRepository")
 * @ORM\HasLifecycleCallbacks
 */
class VulnerabilityReferenceCode extends Base\VulnerabilityReferenceCode implements SystemComponent, HasIdColumn, GeneratesUniqueHash
{
    const REF_TYPE_CVE           = 'cve';
    const REF_TYPE_BID           = 'bid';
    const REF_TYPE_CERT          = 'cert';
    const REF_TYPE_PCI_3_1       = 'pci3.1';
    const REF_TYPE_PCI_3_2       = 'pci3.2';
    const REF_TYPE_OWASP         = 'owasp';
    const REF_TYPE_CAPEC         = 'capec';
    const REF_TYPE_WASC          = 'wasc';
    const REF_TYPE_OSVDBID       = 'osvdbid';
    const REF_TYPE_HIPAA         = 'hipaa';
    const REF_TYPE_ONLINE_OTHER  = 'online_other';
    const REF_TYPE_MANDRIVA      = 'mandriva';
    const REF_TYPE_DEBIAN        = 'debian';
    const REF_TYPE_REDHAT        = 'redhat';
    const REF_TYPE_APPLE         = 'apple';
    const REF_TYPE_XF            = 'xf';
    const REF_TYPE_SUSE          = 'suse';
    const REF_TYPE_DISA_SEVERITY = 'disa_severity';
    const REF_TYPE_IAVM          = 'iavm';
    const REF_TYPE_CERTVN        = 'cert-vn';
    const REF_TYPE_MS            = 'ms';
    const REF_TYPE_MSKB          = 'mskb';

    /**
     * @ORM\ManyToOne(targetEntity="Vulnerability", inversedBy="vulnerabilityReferenceCodes", cascade={"persist"}, fetch="EAGER")
     * @ORM\JoinColumn(name="`vulnerability_id`", referencedColumnName="`id`", nullable=false)
     */
    protected $vulnerability;

    /**
     * @inheritdoc
     *
     * @return User
     */
    public function getUser()
    {
        return $this->vulnerability->getFile()->getUser();
    }

    /**
     * @inheritdoc
     *
     * @param User $user
     * @return $this
     */
    public function setUser(User $user)
    {
        return $this;
    }

    /**
     * Override the parent method to lowercase the input before setting
     *
     * @param string $reference_type
     * @return Base\VulnerabilityReferenceCode
     */
    public function setReferenceType($reference_type)
    {
        $reference_type = strtoupper($reference_type);
        return parent::setReferenceType($reference_type);
    }

    /**
     * Convenience method for returning the parent Vulnerability entity
     *
     * @return Base\Vulnerability
     */
    public function getParent()
    {
        return $this->vulnerability;
    }

    /**
     * Convenience method for setting the parent Vulnerability entity
     *
     * @param Base\Vulnerability $vulnerability
     * @return Base\VulnerabilityReferenceCode
     */
    public function setParent(Base\Vulnerability $vulnerability)
    {
        return parent::setVulnerability($vulnerability);
    }

    /**
     * @return Collection
     */
    public static function getValidRefenceCodeTypes(): Collection
    {
        return new Collection([
            static::REF_TYPE_CVE,
            static::REF_TYPE_BID,
            static::REF_TYPE_CERT,
            static::REF_TYPE_PCI_3_1,
            static::REF_TYPE_PCI_3_2,
            static::REF_TYPE_OWASP,
            static::REF_TYPE_CAPEC,
            static::REF_TYPE_WASC,
            static::REF_TYPE_OSVDBID,
            static::REF_TYPE_HIPAA,
            static::REF_TYPE_ONLINE_OTHER,
            static::REF_TYPE_MANDRIVA,
            static::REF_TYPE_DEBIAN,
            static::REF_TYPE_REDHAT,
            static::REF_TYPE_APPLE,
            static::REF_TYPE_XF,
            static::REF_TYPE_SUSE,
            static::REF_TYPE_DISA_SEVERITY,
            static::REF_TYPE_IAVM,
            static::REF_TYPE_CERTVN,
            static::REF_TYPE_MS,
            static::REF_TYPE_MSKB,
        ]);
    }

    /**
     * Get a regex to match any valid reference code type
     *
     * @return string
     */
    public static function getValidReferenceCodeTypeRegex(): string
    {
        return "/^(" . static::getValidRefenceCodeTypes()->reduce(function ($regex, $refenceCodeType) {
            // Separate by a pipe and escape regex special characters
            return $regex . "|" . preg_quote($refenceCodeType);
        }) . ")$/i";
    }

    /**
     * @inheritdoc
     * @return bool
     */
    public function hasMinimumRequiredPropertiesSet(): bool
    {
        return isset($this->reference_type) && isset($this->value);
    }

    /**
     * Get a hash value of all the property values for all the properties that are set
     *
     * @return string
     */
    public function getHash(): string
    {
        return AbstractEntity::generateUniqueHash($this->getUniqueKeyColumns());
    }

    /**
     * @return Collection
     */
    public function getUniqueKeyColumns(): Collection
    {
        return new Collection([
            parent::REFERENCE_TYPE => $this->reference_type,
            parent::VALUE          => $this->value,
            parent::VULNERABILITY  => $this->vulnerability,
        ]);
    }
}